/*-------------------------------------------------------------------------
 *
 * xactdesc.c
 *	  rmgr descriptor routines for access/transam/xact.c
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/access/rmgrdesc/xactdesc.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/transam.h"
#include "access/xact.h"
#include "replication/origin.h"
#include "storage/sinval.h"
#include "storage/standbydefs.h"
#include "utils/timestamp.h"

/*
 * 将 xact 提交和中止记录的 WAL 格式解析为更易理解的格式。
 *
 * 这些例程位于 xactdesc.c 中，因为它们在后端（回放 WAL）和前端（pg_waldump）代码中被访问。这个文件是唯一一个在两者之间共享的 xact 特定文件。它们复杂到足以让重复变得麻烦。
 */

void ParseCommitRecord(uint8 fc_info, xl_xact_commit *fc_xlrec, xl_xact_parsed_commit *fc_parsed)
{
	char	   *fc_data = ((char *) fc_xlrec) + MinSizeOfXactCommit;

	memset(fc_parsed, 0, sizeof(*fc_parsed));

	fc_parsed->xinfo = 0;			/* 默认，如果没有 XLOG_XACT_HAS_INFO 被
								 * 存在 */

	fc_parsed->xact_time = fc_xlrec->xact_time;

	if (fc_info & XLOG_XACT_HAS_INFO)
	{
		xl_xact_xinfo *fc_xl_xinfo = (xl_xact_xinfo *) fc_data;

		fc_parsed->xinfo = fc_xl_xinfo->xinfo;

		fc_data += sizeof(xl_xact_xinfo);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_DBINFO)
	{
		xl_xact_dbinfo *fc_xl_dbinfo = (xl_xact_dbinfo *) fc_data;

		fc_parsed->dbId = fc_xl_dbinfo->dbId;
		fc_parsed->tsId = fc_xl_dbinfo->tsId;

		fc_data += sizeof(xl_xact_dbinfo);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
	{
		xl_xact_subxacts *fc_xl_subxacts = (xl_xact_subxacts *) fc_data;

		fc_parsed->nsubxacts = fc_xl_subxacts->nsubxacts;
		fc_parsed->subxacts = fc_xl_subxacts->subxacts;

		fc_data += MinSizeOfXactSubxacts;
		fc_data += fc_parsed->nsubxacts * sizeof(TransactionId);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_RELFILENODES)
	{
		xl_xact_relfilenodes *fc_xl_relfilenodes = (xl_xact_relfilenodes *) fc_data;

		fc_parsed->nrels = fc_xl_relfilenodes->nrels;
		fc_parsed->xnodes = fc_xl_relfilenodes->xnodes;

		fc_data += MinSizeOfXactRelfilenodes;
		fc_data += fc_xl_relfilenodes->nrels * sizeof(RelFileNode);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_DROPPED_STATS)
	{
		xl_xact_stats_items *fc_xl_drops = (xl_xact_stats_items *) fc_data;

		fc_parsed->nstats = fc_xl_drops->nitems;
		fc_parsed->stats = fc_xl_drops->items;

		fc_data += MinSizeOfXactStatsItems;
		fc_data += fc_xl_drops->nitems * sizeof(xl_xact_stats_item);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_INVALS)
	{
		xl_xact_invals *fc_xl_invals = (xl_xact_invals *) fc_data;

		fc_parsed->nmsgs = fc_xl_invals->nmsgs;
		fc_parsed->msgs = fc_xl_invals->msgs;

		fc_data += MinSizeOfXactInvals;
		fc_data += fc_xl_invals->nmsgs * sizeof(SharedInvalidationMessage);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
	{
		xl_xact_twophase *fc_xl_twophase = (xl_xact_twophase *) fc_data;

		fc_parsed->twophase_xid = fc_xl_twophase->xid;

		fc_data += sizeof(xl_xact_twophase);

		if (fc_parsed->xinfo & XACT_XINFO_HAS_GID)
		{
			strlcpy(fc_parsed->twophase_gid, fc_data, sizeof(fc_parsed->twophase_gid));
			fc_data += strlen(fc_data) + 1;
		}
	}

	/* 注意：在这一点之后不保证对齐 */

	if (fc_parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
	{
		xl_xact_origin fc_xl_origin;

		/* 不保证对齐，因此复制到栈上 */
		memcpy(&fc_xl_origin, fc_data, sizeof(fc_xl_origin));

		fc_parsed->origin_lsn = fc_xl_origin.origin_lsn;
		fc_parsed->origin_timestamp = fc_xl_origin.origin_timestamp;

		fc_data += sizeof(xl_xact_origin);
	}
}

void ParseAbortRecord(uint8 fc_info, xl_xact_abort *fc_xlrec, xl_xact_parsed_abort *fc_parsed)
{
	char	   *fc_data = ((char *) fc_xlrec) + MinSizeOfXactAbort;

	memset(fc_parsed, 0, sizeof(*fc_parsed));

	fc_parsed->xinfo = 0;			/* 默认，如果没有 XLOG_XACT_HAS_INFO 被
								 * 存在 */

	fc_parsed->xact_time = fc_xlrec->xact_time;

	if (fc_info & XLOG_XACT_HAS_INFO)
	{
		xl_xact_xinfo *fc_xl_xinfo = (xl_xact_xinfo *) fc_data;

		fc_parsed->xinfo = fc_xl_xinfo->xinfo;

		fc_data += sizeof(xl_xact_xinfo);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_DBINFO)
	{
		xl_xact_dbinfo *fc_xl_dbinfo = (xl_xact_dbinfo *) fc_data;

		fc_parsed->dbId = fc_xl_dbinfo->dbId;
		fc_parsed->tsId = fc_xl_dbinfo->tsId;

		fc_data += sizeof(xl_xact_dbinfo);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_SUBXACTS)
	{
		xl_xact_subxacts *fc_xl_subxacts = (xl_xact_subxacts *) fc_data;

		fc_parsed->nsubxacts = fc_xl_subxacts->nsubxacts;
		fc_parsed->subxacts = fc_xl_subxacts->subxacts;

		fc_data += MinSizeOfXactSubxacts;
		fc_data += fc_parsed->nsubxacts * sizeof(TransactionId);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_RELFILENODES)
	{
		xl_xact_relfilenodes *fc_xl_relfilenodes = (xl_xact_relfilenodes *) fc_data;

		fc_parsed->nrels = fc_xl_relfilenodes->nrels;
		fc_parsed->xnodes = fc_xl_relfilenodes->xnodes;

		fc_data += MinSizeOfXactRelfilenodes;
		fc_data += fc_xl_relfilenodes->nrels * sizeof(RelFileNode);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_DROPPED_STATS)
	{
		xl_xact_stats_items *fc_xl_drops = (xl_xact_stats_items *) fc_data;

		fc_parsed->nstats = fc_xl_drops->nitems;
		fc_parsed->stats = fc_xl_drops->items;

		fc_data += MinSizeOfXactStatsItems;
		fc_data += fc_xl_drops->nitems * sizeof(xl_xact_stats_item);
	}

	if (fc_parsed->xinfo & XACT_XINFO_HAS_TWOPHASE)
	{
		xl_xact_twophase *fc_xl_twophase = (xl_xact_twophase *) fc_data;

		fc_parsed->twophase_xid = fc_xl_twophase->xid;

		fc_data += sizeof(xl_xact_twophase);

		if (fc_parsed->xinfo & XACT_XINFO_HAS_GID)
		{
			strlcpy(fc_parsed->twophase_gid, fc_data, sizeof(fc_parsed->twophase_gid));
			fc_data += strlen(fc_data) + 1;
		}
	}

	/* 注意：在这一点之后不保证对齐 */

	if (fc_parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
	{
		xl_xact_origin fc_xl_origin;

		/* 不保证对齐，因此复制到栈上 */
		memcpy(&fc_xl_origin, fc_data, sizeof(fc_xl_origin));

		fc_parsed->origin_lsn = fc_xl_origin.origin_lsn;
		fc_parsed->origin_timestamp = fc_xl_origin.origin_timestamp;

		fc_data += sizeof(xl_xact_origin);
	}
}

/*
 * ParsePrepareRecord
 */
void ParsePrepareRecord(uint8 fc_info, xl_xact_prepare *fc_xlrec, xl_xact_parsed_prepare *fc_parsed)
{
	char	   *fc_bufptr;

	fc_bufptr = ((char *) fc_xlrec) + MAXALIGN(sizeof(xl_xact_prepare));

	memset(fc_parsed, 0, sizeof(*fc_parsed));

	fc_parsed->xact_time = fc_xlrec->prepared_at;
	fc_parsed->origin_lsn = fc_xlrec->origin_lsn;
	fc_parsed->origin_timestamp = fc_xlrec->origin_timestamp;
	fc_parsed->twophase_xid = fc_xlrec->xid;
	fc_parsed->dbId = fc_xlrec->database;
	fc_parsed->nsubxacts = fc_xlrec->nsubxacts;
	fc_parsed->nrels = fc_xlrec->ncommitrels;
	fc_parsed->nabortrels = fc_xlrec->nabortrels;
	fc_parsed->nmsgs = fc_xlrec->ninvalmsgs;

	strncpy(fc_parsed->twophase_gid, fc_bufptr, fc_xlrec->gidlen);
	fc_bufptr += MAXALIGN(fc_xlrec->gidlen);

	fc_parsed->subxacts = (TransactionId *) fc_bufptr;
	fc_bufptr += MAXALIGN(fc_xlrec->nsubxacts * sizeof(TransactionId));

	fc_parsed->xnodes = (RelFileNode *) fc_bufptr;
	fc_bufptr += MAXALIGN(fc_xlrec->ncommitrels * sizeof(RelFileNode));

	fc_parsed->abortnodes = (RelFileNode *) fc_bufptr;
	fc_bufptr += MAXALIGN(fc_xlrec->nabortrels * sizeof(RelFileNode));

	fc_parsed->stats = (xl_xact_stats_item *) fc_bufptr;
	fc_bufptr += MAXALIGN(fc_xlrec->ncommitstats * sizeof(xl_xact_stats_item));

	fc_parsed->abortstats = (xl_xact_stats_item *) fc_bufptr;
	fc_bufptr += MAXALIGN(fc_xlrec->nabortstats * sizeof(xl_xact_stats_item));

	fc_parsed->msgs = (SharedInvalidationMessage *) fc_bufptr;
	fc_bufptr += MAXALIGN(fc_xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage));
}

static void fc_xact_desc_relations(StringInfo fc_buf, char *fc_label, int fc_nrels,
					RelFileNode *fc_xnodes)
{
	int			fc_i;

	if (fc_nrels > 0)
	{
		appendStringInfo(fc_buf, "; %s:", fc_label);
		for (fc_i = 0; fc_i < fc_nrels; fc_i++)
		{
			char	   *fc_path = relpathperm(fc_xnodes[fc_i], MAIN_FORKNUM);

			appendStringInfo(fc_buf, " %s", fc_path);
			pfree(fc_path);
		}
	}
}

static void fc_xact_desc_subxacts(StringInfo fc_buf, int fc_nsubxacts, TransactionId *fc_subxacts)
{
	int			fc_i;

	if (fc_nsubxacts > 0)
	{
		appendStringInfoString(fc_buf, "; subxacts:");
		for (fc_i = 0; fc_i < fc_nsubxacts; fc_i++)
			appendStringInfo(fc_buf, " %u", fc_subxacts[fc_i]);
	}
}

static void fc_xact_desc_stats(StringInfo fc_buf, const char *fc_label,
				int fc_ndropped, xl_xact_stats_item *fc_dropped_stats)
{
	int			fc_i;

	if (fc_ndropped > 0)
	{
		appendStringInfo(fc_buf, "; %sdropped stats:", fc_label);
		for (fc_i = 0; fc_i < fc_ndropped; fc_i++)
		{
			appendStringInfo(fc_buf, " %d/%u/%u",
							 fc_dropped_stats[fc_i].kind,
							 fc_dropped_stats[fc_i].dboid,
							 fc_dropped_stats[fc_i].objoid);
		}
	}
}

static void fc_xact_desc_commit(StringInfo fc_buf, uint8 fc_info, xl_xact_commit *fc_xlrec, RepOriginId fc_origin_id)
{
	xl_xact_parsed_commit fc_parsed;

	ParseCommitRecord(fc_info, fc_xlrec, &fc_parsed);

	/* 如果这是一个预处理的 xact，显示原始 xact 的 xid */
	if (TransactionIdIsValid(fc_parsed.twophase_xid))
		appendStringInfo(fc_buf, "%u: ", fc_parsed.twophase_xid);

	appendStringInfoString(fc_buf, timestamptz_to_str(fc_xlrec->xact_time));

	fc_xact_desc_relations(fc_buf, "rels", fc_parsed.nrels, fc_parsed.xnodes);
	fc_xact_desc_subxacts(fc_buf, fc_parsed.nsubxacts, fc_parsed.subxacts);
	fc_xact_desc_stats(fc_buf, "", fc_parsed.nstats, fc_parsed.stats);

	standby_desc_invalidations(fc_buf, fc_parsed.nmsgs, fc_parsed.msgs, fc_parsed.dbId,
							   fc_parsed.tsId,
							   XactCompletionRelcacheInitFileInval(fc_parsed.xinfo));

	if (XactCompletionApplyFeedback(fc_parsed.xinfo))
		appendStringInfoString(fc_buf, "; apply_feedback");

	if (XactCompletionForceSyncCommit(fc_parsed.xinfo))
		appendStringInfoString(fc_buf, "; sync");

	if (fc_parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
	{
		appendStringInfo(fc_buf, "; origin: node %u, lsn %X/%X, at %s",
						 fc_origin_id,
						 LSN_FORMAT_ARGS(fc_parsed.origin_lsn),
						 timestamptz_to_str(fc_parsed.origin_timestamp));
	}
}

static void fc_xact_desc_abort(StringInfo fc_buf, uint8 fc_info, xl_xact_abort *fc_xlrec, RepOriginId fc_origin_id)
{
	xl_xact_parsed_abort fc_parsed;

	ParseAbortRecord(fc_info, fc_xlrec, &fc_parsed);

	/* 如果这是一个预处理的 xact，显示原始 xact 的 xid */
	if (TransactionIdIsValid(fc_parsed.twophase_xid))
		appendStringInfo(fc_buf, "%u: ", fc_parsed.twophase_xid);

	appendStringInfoString(fc_buf, timestamptz_to_str(fc_xlrec->xact_time));

	fc_xact_desc_relations(fc_buf, "rels", fc_parsed.nrels, fc_parsed.xnodes);
	fc_xact_desc_subxacts(fc_buf, fc_parsed.nsubxacts, fc_parsed.subxacts);

	if (fc_parsed.xinfo & XACT_XINFO_HAS_ORIGIN)
	{
		appendStringInfo(fc_buf, "; origin: node %u, lsn %X/%X, at %s",
						 fc_origin_id,
						 LSN_FORMAT_ARGS(fc_parsed.origin_lsn),
						 timestamptz_to_str(fc_parsed.origin_timestamp));
	}

	fc_xact_desc_stats(fc_buf, "", fc_parsed.nstats, fc_parsed.stats);
}

static void fc_xact_desc_prepare(StringInfo fc_buf, uint8 fc_info, xl_xact_prepare *fc_xlrec, RepOriginId fc_origin_id)
{
	xl_xact_parsed_prepare fc_parsed;

	ParsePrepareRecord(fc_info, fc_xlrec, &fc_parsed);

	appendStringInfo(fc_buf, "gid %s: ", fc_parsed.twophase_gid);
	appendStringInfoString(fc_buf, timestamptz_to_str(fc_parsed.xact_time));

	fc_xact_desc_relations(fc_buf, "rels(commit)", fc_parsed.nrels, fc_parsed.xnodes);
	fc_xact_desc_relations(fc_buf, "rels(abort)", fc_parsed.nabortrels,
						fc_parsed.abortnodes);
	fc_xact_desc_stats(fc_buf, "commit ", fc_parsed.nstats, fc_parsed.stats);
	fc_xact_desc_stats(fc_buf, "abort ", fc_parsed.nabortstats, fc_parsed.abortstats);
	fc_xact_desc_subxacts(fc_buf, fc_parsed.nsubxacts, fc_parsed.subxacts);

	standby_desc_invalidations(fc_buf, fc_parsed.nmsgs, fc_parsed.msgs, fc_parsed.dbId,
							   fc_parsed.tsId, fc_xlrec->initfileinval);

	/*
	 * 检查复制源是否已在此记录中以与 PrepareRedoAdd() 
	 * 相同的方式设置。
	 */
	if (fc_origin_id != InvalidRepOriginId)
		appendStringInfo(fc_buf, "; origin: node %u, lsn %X/%X, at %s",
						 fc_origin_id,
						 LSN_FORMAT_ARGS(fc_parsed.origin_lsn),
						 timestamptz_to_str(fc_parsed.origin_timestamp));
}

static void fc_xact_desc_assignment(StringInfo fc_buf, xl_xact_assignment *fc_xlrec)
{
	int			fc_i;

	appendStringInfoString(fc_buf, "subxacts:");

	for (fc_i = 0; fc_i < fc_xlrec->nsubxacts; fc_i++)
		appendStringInfo(fc_buf, " %u", fc_xlrec->xsub[fc_i]);
}

void xact_desc(StringInfo fc_buf, XLogReaderState *fc_record)
{
	char	   *fc_rec = XLogRecGetData(fc_record);
	uint8		fc_info = XLogRecGetInfo(fc_record) & XLOG_XACT_OPMASK;

	if (fc_info == XLOG_XACT_COMMIT || fc_info == XLOG_XACT_COMMIT_PREPARED)
	{
		xl_xact_commit *fc_xlrec = (xl_xact_commit *) fc_rec;

		fc_xact_desc_commit(fc_buf, XLogRecGetInfo(fc_record), fc_xlrec,
						 XLogRecGetOrigin(fc_record));
	}
	else if (fc_info == XLOG_XACT_ABORT || fc_info == XLOG_XACT_ABORT_PREPARED)
	{
		xl_xact_abort *fc_xlrec = (xl_xact_abort *) fc_rec;

		fc_xact_desc_abort(fc_buf, XLogRecGetInfo(fc_record), fc_xlrec,
						XLogRecGetOrigin(fc_record));
	}
	else if (fc_info == XLOG_XACT_PREPARE)
	{
		xl_xact_prepare *fc_xlrec = (xl_xact_prepare *) fc_rec;

		fc_xact_desc_prepare(fc_buf, XLogRecGetInfo(fc_record), fc_xlrec,
						  XLogRecGetOrigin(fc_record));
	}
	else if (fc_info == XLOG_XACT_ASSIGNMENT)
	{
		xl_xact_assignment *fc_xlrec = (xl_xact_assignment *) fc_rec;

		/*
		 * 注意，我们忽略 WAL 记录的 xid，因为我们更
		 * 感兴趣的是发出记录的顶层 xid 以及
		 * 这里报告的 xids。
		 */
		appendStringInfo(fc_buf, "xtop %u: ", fc_xlrec->xtop);
		fc_xact_desc_assignment(fc_buf, fc_xlrec);
	}
	else if (fc_info == XLOG_XACT_INVALIDATIONS)
	{
		xl_xact_invals *fc_xlrec = (xl_xact_invals *) fc_rec;

		standby_desc_invalidations(fc_buf, fc_xlrec->nmsgs, fc_xlrec->msgs, InvalidOid,
								   InvalidOid, false);
	}
}

const char * xact_identify(uint8 fc_info)
{
	const char *fc_id = NULL;

	switch (fc_info & XLOG_XACT_OPMASK)
	{
		case XLOG_XACT_COMMIT:
			fc_id = "COMMIT";
			break;
		case XLOG_XACT_PREPARE:
			fc_id = "PREPARE";
			break;
		case XLOG_XACT_ABORT:
			fc_id = "ABORT";
			break;
		case XLOG_XACT_COMMIT_PREPARED:
			fc_id = "COMMIT_PREPARED";
			break;
		case XLOG_XACT_ABORT_PREPARED:
			fc_id = "ABORT_PREPARED";
			break;
		case XLOG_XACT_ASSIGNMENT:
			fc_id = "ASSIGNMENT";
			break;
		case XLOG_XACT_INVALIDATIONS:
			fc_id = "INVALIDATION";
			break;
	}

	return fc_id;
}
